/*
 * metronome_gui.cpp
 *
 *  Created on: Dec 22, 2014
 *      Author: fura
 */

#include <string>
#include <iostream>

#include <gtkmm.h>

// include the URI and global data of this plugin
#include "widget.h"

#include "lv2/lv2plug.in/ns/lv2core/lv2.h"

// GUI
#include "lv2/lv2plug.in/ns/extensions/ui/ui.h"
#include "metronome.h"
#include "metronome.h"


using namespace std;

typedef struct {
	MetronomeWidget* widget;
    LV2_Atom_Sequence* control;
    MetroURIs uris;
} MetronomeGUI;

static GtkWidget* make_gui(MetronomeGUI *self) {

    // setup GTKmm for QT hosts, GTK hosts will have this done already
    // no harm in calling it again :)
    Gtk::Main::init_gtkmm_internals();

    // Create a container using the C api
    GtkWidget* container = gtk_vbox_new(FALSE, 2);

    // create a C++ widget which we will draw the UI in
    self->widget = new MetronomeWidget();

    // get the C version of the widgtet
    // this step is nessiary because we are using the C++ wrapper around
    // GTK, and the LV2 spec requires us to pass the C version of the
    // widget to the host. Its easy, so that's no problem!
    GtkWidget* cWidget = (GtkWidget*)self->widget->gobj();


    // add the C widget to the container
    cout << "adding to window!" << endl;
    gtk_container_add( (GtkContainer*)container, cWidget );

    return container;
}


static LV2UI_Handle instantiate(const struct _LV2UI_Descriptor * descriptor,
                const char * plugin_uri,
                const char * bundle_path,
                LV2UI_Write_Function write_function,
                LV2UI_Controller controller,
                LV2UI_Widget * widget,
                const LV2_Feature * const * features) {

    if (strcmp(plugin_uri, METRONOME_URI) != 0) {
        fprintf(stderr, "SORCER_URI error: this GUI does not support plugin with URI %s\n", plugin_uri);
        return NULL;
    }


    MetronomeGUI* self = (MetronomeGUI*)malloc(sizeof(MetronomeGUI));

    cout << "Allocated SourceGUI!" << endl;

    if (self == NULL) return NULL;

    	// Scan host features for URID map
	LV2_URID_Map* map = NULL;
	for (int i = 0; features[i]; ++i) {
		if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) {
			map = (LV2_URID_Map*)features[i]->data;
		}
	}
	if (!map) {
		fprintf(stderr, "Host does not support urid:map.\n");
		free(self);
		return NULL;
	}

	map_uris(map, &self->uris);


    cout << "Creating UI!" << endl;
    *widget = (LV2UI_Widget)make_gui(self);

    cout << "Writing controller f(x)!" << endl;

    self->widget->controller = controller;
    self->widget->write_function = write_function;

    cout << "returning..." << endl;

    return (LV2UI_Handle)self;
}

static void cleanup(LV2UI_Handle ui) {
    printf("cleanup()\n");
    MetronomeGUI *pluginGui = (MetronomeGUI *) ui;
    free(pluginGui);
}

static void port_event(LV2UI_Handle ui,
               uint32_t port_index,
               uint32_t buffer_size,
               uint32_t format,
               const void * buffer)
{
	MetronomeGUI *self = (MetronomeGUI *) ui;
	const LV2_Atom* atom = (const LV2_Atom*)buffer;

	if (port_index == METRO_NOTIFY) {
		self->control = (LV2_Atom_Sequence*)buffer;

		if (atom->type == self->uris.atom_Blank || atom->type == self->uris.atom_Object) {
			const LV2_Atom_Object* obj = (const LV2_Atom_Object*)atom;
			if (obj->body.otype == self->uris.time_Position) {
				const LV2_Atom* atom_msg = NULL;
				lv2_atom_object_get(obj, self->uris.time_speed, &atom_msg, NULL);
				const int32_t msg =  ((const LV2_Atom_Int*)atom_msg)->body;
				self->widget->update(msg);
				//cout << "Received: " << msg << endl;
			}
		}
	}

	return;
}

static LV2UI_Descriptor descriptors[] = {
    {METRONOME_UI_URI, instantiate, cleanup, port_event, NULL}
};

const LV2UI_Descriptor * lv2ui_descriptor(uint32_t index) {
    printf("lv2ui_descriptor(%u) called\n", (unsigned int)index);
    if (index >= sizeof(descriptors) / sizeof(descriptors[0])) {
        return NULL;
    }
    return descriptors + index;
}



